# N, V, ADJ, and ADV
import nltk

from nltk.corpus import wordnet as wn
from nltk.corpus import verbnet as vn

import rai.sem.parse_util as parse_util

from rai.logger import *

from xml.dom.minidom import parseString
from xml.etree import ElementTree

import en

class VerbnetUtil:
    
    def __init__(self):
        self.stemmer = nltk.PorterStemmer()
        pass
    
    def getInf(self, verb):
        return en.verb.infinitive(verb)
    
    def getVNClassIDs(self, verb):
        """
            Returns a list of classids
        """
        return vn.classids(lemma=verb)
    
        
    def getVNClass(self, vnclass_id):
        """
            Returns the class matching the id
        """
        vnclass = vn.vnclass(vnclass_id) # get xml representation
        return vnclass
    
    def __createVNFrameWrapper(self, vnframe, vnclass_id):
        """
        return a VNFrameWrapper class which provides easy access to vnframe attributes
        """
        vnframeClass = VNFrameWrapper()
        vnframeClass.vnclass_id = vnclass_id
        descr = vnframe.find('DESCRIPTION') 
        vnframeClass.desc1 = descr.attrib['primary']
        if descr.get('secondary', ''): 
            vnframeClass.desc2 = descr.get('secondary')

        chunks = []                
        for elt in vnframe.find('SYNTAX'):
            value = None
            if 'value' in elt.attrib:
                value = elt.get('value')
            type = convertVerbnetTypeToChunks(elt.tag)
            syntaxPart = SyntaxPart(type, value)
            #chunks.append(convertVerbnetTypeToChunks(elt.tag))
            chunks.append(syntaxPart)            
        vnframeClass.syntax = chunks
        
        preds = [] # list of semantic preds
        pieces = []
        for pred in vnframe.findall('SEMANTICS/PRED'): 
            args = [(arg.get('value'), arg.get('type')) for arg in pred.findall('ARGS/ARG')]
            
            args_formatted = map(lambda x: "%s[%s]" % (x[0], x[1]), args)
            pieces.append('%s(%s)' % (pred.get('value'), ', '.join(args_formatted)))
            
            name = str(pred.get('value'))
            args = [VNSemanticArgWrapper(arg.get('value'), arg.get('type')) for arg in pred.findall('ARGS/ARG')]

            pred = VNSemanticWrapper(name, args)
            #preds.append(pred_args_tuple)
            preds.append(pred)
        vnframeClass.sem = '\n'.join(['* %s' % (piece) for piece in pieces])
        vnframeClass.preds = preds
        return vnframeClass
    
    def getVerbnetFrames(self, verb):
        """
            Returns list of frames
            @return: [VNFrameWrapper, VNFrameWrapper, ...]
        """
        classids = vn.classids(lemma=verb)
        frames = []
        if (len(classids) > 0):
            for vnclass_id in vn.classids(lemma=verb):
                vnclass = vn.vnclass(vnclass_id) # get xml representation
                for vnframe in vnclass.findall('FRAMES/FRAME'):
    
                    frame = self.__createVNFrameWrapper(vnframe, vnclass.get('ID'))
                    frames.append(frame)
                
        return frames
    
    def pprintVNClass(self, verb):
        """
            Pretty prints the verbnet entries for the verb
        """
        classids = vn.classids(lemma=verb)
        for classid in classids:
            print vn.pprint(classid)
    
    def getStem(self, verb):
        return self.stemmer.stem(verb)
    
    def isVerbnetFrameMatch(self, frameWrapper, parsedSentence):
        """
            Returns true if the parsed sentence matches the vnframe syntactic structure
            @return: (isMatch, matchMessage)
        """
        if parsedSentence is None or frameWrapper is None:
            return (False, None)
        
        syntax = frameWrapper.syntax
        
        if len(syntax) > len(parsedSentence):
            # syntax definition has more parts than the sentence
            return (False, None)
        
        # syntax = [SyntaxPart, SyntaxPart, ...]
        # syntax has same length as parts in the sentence
        if len(syntax) == len(parsedSentence):
            syntax_index = 0
            for index, part in enumerate(parsedSentence):
                type = part[0] # first element of sentence part defines the type of the part (e.g.: NP/VP)
                syntaxPart = syntax[syntax_index] # get the SyntaxPart at the next position
                if (syntaxPart.matchesPart(part)):
                    #types are the same, try to match the next part
                    syntax_index = syntax_index + 1
                else:
                    # skip the part
                    return (False, None)
                    pass            
            
            #matcher = zip(map(lambda x: x.type, syntax), map(lambda x: x[0], parsedSentence))
            #matcher = [(index, element) for index, element in enumerate(matcher)]
            #mismatch = filter(lambda x: x[1][0] != x[1][1], matcher)
            #isMatch = (len(mismatch) == 0)
            # count if all the part Types are the same
            
            return (True, None) 
            #print matcher
        # loop over the sentence parts,

        return (False, None)
        for index, part in enumerate(parsedSentence):
            type = part[0] # first element of sentence part defines the type of the part (e.g.: NP/VP)
    
    def getSyntaxStructures(self, parsed_sentence, trace = 0):
        """
            Returns a list of tuples containing the frame and the match.
            The first item of the tuple is a VNFrameWrapper, 
            the second item is a list of SyntaxParts describing the match with the frame.
            
            @return: a list of tuples: [(frame, match), (frame, match)]
        """
        structs = []
        # get the first verb of the first VerbPart
        verbParts = parse_util.getParts('VP', parsed_sentence) 
        verbPart = verbParts[0]
        verb_tuples = verbPart[1:]
        
        v_tuple = verb_tuples[0]
        verb = v_tuple[0]
        verb_inf = self.getInf(verb) # convert the verb to its infinitive

        if trace == 5:
            print 10*'=', "pretty print verbnet info", 10*'='
            self.pprintVNClass(verb_inf)
        # look up the matching frames in Verbnet    
        frames = self.getVerbnetFrames(verb_inf)
        for frame in frames: 
            (isMatch, misMatch) = self.isVerbnetFrameMatch(frame, parsed_sentence)
            if isMatch:
                # the match can still fail because a frame may define specific words for a PREP
                # e.g.: 
                # sentence:
                #[['NP', ('Bob', 'NP', 'NNP')]
                #, ['VP', ('lives', 'VBZ', 'VBZ')]
                #, ['PP', ('in', 'IN', 'IN')]
                #, ['NP', ('a', 'AT', 'DT'), ('house', 'NN', 'NN')]]
                # matches the syntax of gorge, but PREP[on] does not match PP('in')
                # * gorge-39.6 
                #    frame = PP (Patient-PP)
                #        syntax = NP[Agent] VERB PREP[on] NP[Patient]
                # however it does match lodge
                # * lodge-46
                #    frame = PP (Location-PP)
                #        syntax = NP[Theme] VERB PREP[at in with] NP[Location]
                match = self.matchVerbnetSyntax(frame, parsed_sentence, trace=trace)
                #print match
                structs.append((frame, match))
        
        return structs
                
    def matchVerbnetSyntax(self, frameWrapper, parsedSentence, trace=0):
        """
            Returns a list of parts (of the parsedSentence) that matched the frameWrapper.
            
            @param frameWrapper: a VNFrameWrapper
            @param parsedSentence: the parsed sentence
            @return: a list of matched parts: [(syntaxPart, part), (syntaxPart, part), ...]
                    syntaxPart = SyntaxPart
                    part = ['NP', ('a', 'AT', 'DT'), ('house', 'NN', 'NN')]
        """
        if parsedSentence is None:
            return
        #debug(syntax)
        #debug(parsedSentence)
        # try to match the syntax with the parsedSentence
        
        # loop over the parts in the parsedSentence
        syntax = frameWrapper.syntax
        
        syntax_index = 0
        matched_parts = []
        out_of_bounds_count = 0
        for index, part in enumerate(parsedSentence):
            type = part[0] # first element of sentence part defines the type of the part (e.g.: NP/VP)
            if (syntax_index >= len(syntax)):
                # out of bounds in the syntax list
                pass
                out_of_bounds_count = out_of_bounds_count + 1
            else:
                syntaxPart = syntax[syntax_index]
                if (syntaxPart.matchesPart(part)):
                    #types are the same, try to match the next part
                    syntax_index = syntax_index + 1
                    matched_parts.append((syntaxPart, part))
                else:
                    # skip the part
                    pass
        #print "index: %s, syntax.length: %s, out_of_bounds_count: %s" % (syntax_index, len(syntax), out_of_bounds_count)
        return matched_parts   
   
   
class VNFrameWrapper:
    """
        A wrapper class around a vnframe from verbnet.
        TODO: sample sentences extraction from verbnet
    """
    
    def __init__(self):
        self.vnclass_id = None
        self.desc1 = None # string
        self.desc2 = None # string
        self.syntax = None # [SyntaxPart, SyntaxPart, ...]
        self.sem = None
        self.preds = None # [("pred1", [arg1, arg2]), ()]
        
    def __repr__(self):
        return "VNFrameWrapper<%s, %s>" % (self.vnclass_id, self.desc1)
    
    def __str__(self):
        lines = []
        if self.vnclass_id is not None:
            lines.append("vnclass_id: %s" % (self.vnclass_id))
        if self.desc1 is not None:
            lines.append("primary description: %s" % (self.desc1))
        if self.desc2 is not None:
            lines.append("secondary description: %s" % (self.desc2))
        if self.syntax is not None:
            lines.append("syntax: %s" % (map(lambda x: str(x), self.syntax)))
        if self.sem is not None:
            lines.append("sem: %s" % (self.sem))
        if self.preds is not None:
            print str(self.preds)
        return '\n'.join(lines)
    
class VNSemanticWrapper:
    """
        A wrapper class around a semantic definition of a vnframe
    """
    
    def __init__(self, name, args):
        """
            @param name: the name of the predicate
            @param args: a list of args
        """
        self.name = name
        self.args = args
        
    def __repr__(self):
        line = "VNSemanticWrapper<'%s', %s>" % (self.name, self.args)
        return line
            
    def __str__(self):
        line = "VNSemanticWrapper<'%s', %s>" % (self.name, self.args)
        return line
    
    def __eq__(self, other):
        if self.name != other.name:
            return False
        else:
            return self.args == other.args
        
class VNSemanticArgWrapper:
    """
        A wrapper class around an arg in a predicate of a VNFrame
    """

    def __init__(self, name, type):
        """
            @param name: the name of the arg. E.g.: Theme, Agent, Location
            @param type: the type of the arg. E.g.: Event, ThemRole, 
        """
        self.name = name
        self.type = type
        
    def __str__(self):
        line = "Arg<%s,%s>" % (self.name, self.type)
        return line
    
    def __repr__(self):
        line = "Arg<%s,%s>" % (self.name, self.type)
        return line
    
    def __eq__(self, other):
        return self.name == other.name and self.type == other.type 
                
class Part:
    # ['NP', ('Tina', 'NN', 'NN'), ('Turner', 'NP', 'NNP')]
    def __init__(self, part):
        self.type = part[0]
        self.words = part[1:-1]

class SyntaxPart:
    def __init__(self, type, value):
        self.type = type
        self.value = value
        
    def __str__(self):
        return "SyntaxPart<%s, %s>" % (self.type, self.value)
    
    def __repr__(self):
        return "SyntaxPart<%s, %s, %s>" % (self.type, self.value, self.getValues())
    
    def getValues(self):
        """
            splits the self.value string and converts it to a list
        """
        if self.value is None:
            return []
        else:
            return self.value.split();
        
    def matchesPart(self, part):
        """
            Returns true if part matches the SyntaxPart
            part is ['NP', ('a', 'AT', 'DT'), ('house', 'NN', 'NN')]
        """
        type = part[0]
        if not (type == self.type):
            return False
        else:
            if self.type == "PP":
                wordDescriptors = part[1:]
                words = map(lambda x: x[0], wordDescriptors)
                if len(words) == 1:
                    # only 1 word in the PP part
                    if words[0] in self.getValues():
                        #print "" + str(words[0]) + " is in :" + str(self.getValues())
                        return True
                return False
            else:
                # other type than PP
                return True    
                
            

def convertVerbnetTypeToChunks(type):
    """
        Converts the Verbnet to a chunk type.
        E.g.: VERB -> VP, PREP -> PP
    """
    if type == '':
        return ''
    elif type == 'VERB':
        return 'VP'
    elif type == 'PREP':
        return 'PP'
    else:
        return type
    
def getVerbnetSyntaxList(verb):
    classids = vn.classids(lemma=verb)
    if (len(classids) > 0):
        vnclass_id = vn.classids(lemma=verb)[0]
        vnclass = vn.vnclass(vnclass_id) # get xml representation
        
        frames = []
        for vnframe in vnclass.findall('FRAMES/FRAME'):
            chunks = []
            for elt in vnframe.find('SYNTAX'):
                chunks.append(convertVerbnetTypeToChunks(elt.tag))
            frames.append(chunks)
        return frames
                
    else:
        return []
    
def prettyPrint(element):
    txt = ElementTree.tostring(element)
    print parseString(txt).toprettyxml()

def testWordnetWord():
    print "testWordnetWord"
    test_word = 'birthday'
    print N[test_word]
    print N.word(test_word)
    print N[test_word].synsets()
    print N['dog'][0].path_similarity(N['chicken'][0])
    
def testWordnetVerb():    
    print "testWordnetVerb"
    test_word = 'be'
    print V[test_word]
    print V.word(test_word)
    print V[test_word].synsets()
    
def testVerbnet():
    print "testVerbnet"
    # http://nltk.org/doc/api/nltk.corpus.reader.verbnet.VerbnetCorpusReader-class.html   
    # http://nltk.googlecode.com/svn/trunk/doc/howto/corpus.html 
    #print verbnet.classids(lemma='keep')
    #print verbnet.pprint(verbnet.classids(lemma='keep')[0])

    verb = 'believed'
    print "inf: " + en.verb.infinitive(verb)    
    testShowVerbnetVerb(en.verb.infinitive(verb))
    print "Stem: " + returnStem(verb) 


    #testShowVerbnetVerb('performing')
    #print "Stem: " + returnStem('performing')    
     
    testShowVerbnetVerb('perform')
    print "Stem: " + returnStem('perform') 
       
    #testShowVerbnetVerb('live')
    #print "Stem: " + returnStem('live')

def testShowVerbnetVerb(verb):
    #http://nltk.org/doc/guides/coverage/nltk.corpus.reader.verbnet.html
    print "== Verb: " + verb + " =="
    classids = vn.classids(lemma=verb)
    if (len(classids) > 0):    
        print "All verbnet classids:"
        print vn.classids(lemma=verb)
        vnclass_id = vn.classids(lemma=verb)[0]
        print "vnclass_id: " + vnclass_id
        vnclass = vn.vnclass(vnclass_id) # get xml representation
        #print verbnet.pprint(vnclass_id)
        print "ID:" + vnclass.get('ID')
        # get the frames
        print '  Frames:'
        indent = '\t'
        for vnframe in vnclass.findall('FRAMES/FRAME'):
            # a frame...
            # print the description of this frame
            print vn.pprint_description(vnframe, indent)
            descr = vnframe.find('DESCRIPTION') # description tag
            descr.attrib['primary'] # primary
            descr.get('secondary') # secondary
            

            # print the syntax of this frame
            print '== Syntax' 
            print vn.pprint_syntax(vnframe, indent+'  Syntax: ')
            pieces = []
            chunks = []
            for elt in vnframe.find('SYNTAX'):
                # loop over each element, the tagname defines the type (e.g.: NP, VERB, PP)
                piece = elt.tag
                chunks.append(elt.tag)
                modifiers = []
                if 'value' in elt.attrib:
                    modifiers.append(elt.get('value'))
                modifiers += ['%(Value)s%(type)s' % restr.attrib
                              for restr in (elt.findall('SELRESTRS/SELRESTR') +
                                            elt.findall('SYNRESTRS/SYNRESTR'))]
                if modifiers:
                    piece += '[%s]' % ' '.join(modifiers)
                pieces.append(piece)
            
            ' '.join(pieces)
            print ' '.join(chunks)

            # print the semanctics
            print '==  Semantics:'
            print vn.pprint_semantics(vnframe, indent+'    ')
            
        #s = '\n'.join(verbnet.pprint_frame(vnframe, indent='    ')
        #               for vnframe in vnclass.findall('FRAMES/FRAME'))
        #print s
        #print verbnet.vnclass(verbnet.classids(lemma=verb)[0])
        #help(verbnet.vnclass(verbnet.classids(lemma=verb)[0]))
        #print type(verbnet.vnclass(verbnet.classids(lemma=verb)[0]))
        #print str(verbnet.vnclass(verbnet.classids(lemma=verb)[0]))
        #print dir(verbnet.vnclass(verbnet.classids(lemma=verb)[0]))
        #print info(verbnet.vnclass(verbnet.classids(lemma=verb)[0]))
        prettyPrint(vnclass)

def returnStem(verb):
    #from nltk.stem.porter import *
    stemmer = nltk.stem.porter.PorterStemmer()
    return stemmer.stem(verb)

def testStemmer():
    print "testStemmer"
    stemmer = nltk.PorterStemmer()
    verbs = ['appears', 'appear', 'appeared', 'calling', 'called']
    stems = [stemmer.stem(verb) for verb in verbs]
    print stems
    verbs = ['is', 'was', 'had', 'been', 'giving', 'could']
    stems = [stemmer.stem(verb) for verb in verbs]
    print stems    

#print verbnet.classids('pack')
#print nltk.corpus.verbnet.classids('pack')
#nltk.corpus.verbnet._index()
#print nltk.corpus.verbnet.classids('spray')
#print "sample"

if __name__ == "__main__":
    # http://nltk.org/doc/en/ch11.html
    testVerbnet()
    print "lem"
    print vn.lemmas()[20:25]
    for lem in vn.lemmas()[20:25]:
        print vn.classids(lemma=lem)
        print " *"
        for c in vn.classids(lemma=lem):
            #print verbnet.vnclass(c)
            print "sid " + str(vn.shortid(c))
            print vn.pprint(c)
            
    print "********************"
    vn_45_4 = vn.vnclass('45.4')
    for themrole in vn_45_4.findall('THEMROLES/THEMROLE'):
        #print help(themrole)
        print themrole.attrib['type']
        
    testShowVerbnetVerb("love")
    testShowVerbnetVerb("walk")










